﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NUnitResults
{
    /// <summary>
    /// Represents a single test case.
    /// </summary>
    [System.Diagnostics.DebuggerDisplay("TestCase(Name = {Name}, Status = {Status})")]
    public class TestCase : ITestResult
    {
        #region Statics

        /// <summary>
        /// The value of the InnerCases property for all TestCase objects.
        /// </summary>
        private static readonly IList<ITestResult> innerCases = Enumerable.Empty<ITestResult>().ToList();

        #endregion

        #region Properties

        /// <summary>
        /// Gets this ITestResult's inner cases.
        /// </summary>
        public IList<ITestResult> InnerCases { get { return innerCases; } }

        /// <summary>
        /// Gets the name of the test case.
        /// </summary>
        public String Name { get; private set; }

        /// <summary>
        /// Gets the type of this test result.
        /// </summary>
        public SuiteType SuiteType
        {
            get { return SuiteType.TestCase; }
        }

        /// <summary>
        /// Gets the full name of this test case.
        /// </summary>
        public String FullName
        {
            get
            {
                if (String.IsNullOrEmpty(Namespace))
                {
                    return Name;
                }
                if (String.IsNullOrEmpty(Name))
                {
                    return Namespace;
                }

                return String.Format("{0}.{1}", Namespace, Name);
            }
        }

        /// <summary>
        /// Gets the namespace of this test case.
        /// </summary>
        public String Namespace { get; private set; }

        /// <summary>
        /// Gets the status of the test case.
        /// </summary>
        public Status Status { get; private set; }

        /// <summary>
        /// Gets the number of test cases (1).
        /// </summary>
        public TestCaseCount TestCaseCount { get; private set; }

        /// <summary>
        /// Gets the time that this case took to run, if it ran.
        /// </summary>
        public Nullable<TimeSpan> Time { get; private set; }

        /// <summary>
        /// Gets the number of asserts in this test case.
        /// </summary>
        public Nullable<Int32> Asserts { get; private set; }

        #endregion

        #region Constructor

        /// <summary>
        /// Creates a new test case.
        /// </summary>
        /// <param name="name">The name of the test case.</param>
        /// <param name="status">The status of the test case.</param>
        public TestCase(String name, String nspace, Status status, Nullable<TimeSpan> time, Nullable<Int32> asserts)
        {
            if (name == null)
            {
                throw new ArgumentNullException("name");
            }
            if (nspace == null)
            {
                throw new ArgumentNullException("nspace");
            }

            Name = name;
            Namespace = nspace;
            Status = status;
            TestCaseCount = new TestCaseCount(new Dictionary<Status, Int32> { { status, 1 } });
            Time = time;
            Asserts = asserts;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Applies the supplied action to this instance.
        /// </summary>
        /// <param name="result"></param>
        /// <param name="action"></param>
        public void Iterate(Action<ITestResult> action)
        {
            if (action == null)
            {
                throw new ArgumentNullException("action");
            }

            action(this);
        }

        #endregion

        #region Object Overrides

        /// <summary>
        /// Converts this test case to a string.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return String.Format("TestCase(\"{0}\", \"{1}\", \"{2}\", \"{3}\", \"{4}\")", 
                Name, 
                Namespace, 
                Status, 
                Time.HasValue ? Time.ToString() : "[null]", 
                Asserts.HasValue ? Asserts.ToString() : "[null]");
        }

        /// <summary>
        /// Checks if this TestCase is equal to another object.
        /// </summary>
        /// <param name="obj">The object to compare to.</param>
        /// <returns>True if the supplied object is equal to this one,
        /// false otherwise.</returns>
        public override bool Equals(object obj)
        {
            var other = obj as TestCase;

            if (other == null)
            {
                return false;
            }

            if (this.Name != other.Name)
            {
                return false;
            }

            if (this.Namespace != other.Namespace)
            {
                return false;
            }

            if (this.Status != other.Status)
            {
                return false;
            }

            if (this.Time != other.Time)
            {
                return false;
            }

            if (this.Asserts != other.Asserts)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// Gets a hashcode for this object.
        /// </summary>
        /// <returns>The object's hash code.</returns>
        public override int GetHashCode()
        {
            return 
                Name.GetHashCode() ^ 
                Namespace.GetHashCode() ^ 
                Status.GetHashCode() ^ 
                Time.GetHashCode() ^ 
                Asserts.GetHashCode();
        }

        #endregion
    }
}
